<!DOCTYPE html>
<html>
<head>
    <title>DeepSeek Chat</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
    <style>
        :root {
            --user-bg: #f0f7ff;
            --assistant-bg: #f5f5f5;
            --border-radius: 12px;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background-color: bisque; // #f8f9fa
            margin: 0;
            padding: 20px;
        }

        .chat-container {
            max-width: 800px;
            margin: 0 auto;
            background: burlywood; // white
            border-radius: var(--border-radius);
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
            padding: 24px;
        }

        #response {
            height: 60vh;
            overflow-y: auto;
            padding: 16px;
            background: #fcfcfc;
            border-radius: var(--border-radius);
            margin-bottom: 20px;
            border: 1px solid #eee;
            overflow-anchor: none; /* 禁用浏览器默认滚动锚定 */
            overflow-anchor: none; /* 禁用浏览器默认滚动锚定 */
        }

        .message {
            margin: 12px 0;
            display: flex;
        }

        .user-message {
            justify-content: flex-end;
        }

        .message-bubble {
            max-width: 80%;
            padding: 12px 16px;
            border-radius: var(--border-radius);
            line-height: 1.5;
            overflow-x: auto;
            overflow-anchor: auto; /* 只在消息气泡上启用锚定 */
        }
        .message-bubble {
            transition: transform 0.3s ease-out;
            will-change: transform;
        }

        #response {
            scroll-behavior: smooth;
            transform: translateZ(0); /* 启用硬件加速 */
        }

        .user-message .message-bubble {
            background: var(--user-bg);
            border: 1px solid #cce0ff;
            color: #1a5fb4;
        }

        .assistant-message .message-bubble {
            background: var(--assistant-bg);
            border: 1px solid #e0e0e0;
            color: green; //#333
        }

        #input-container {
            position: relative;
            display: flex; /* 使用 flexbox 布局 */
            align-items: flex-end; /* 按钮与输入框底部对齐 */
            border: 1px solid #ddd;
            border-radius: 8px;
            padding: 12px;
            background: white;
        }

        #input {
            flex: 1; /* 占据剩余空间 */
            min-height: 48px;
            max-height: 150px; /* 限制最大高度 */
            border: none;
            outline: none;
            resize: none;
            font-size: 16px;
            line-height: 1.5;
            overflow-y: auto;
            padding-right: 120px; /* 为按钮留出空间 */
            margin-right: 10px; /* 按钮与输入框之间的间距 */
        }

        button {
            position: absolute;
            right: 16px;
            bottom: 16px;
            background: #007bff;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
            transition: background 0.2s;
        }
        button {
            flex-shrink: 0; /* 防止按钮被压缩 */
            margin-left: 8px; /* 按钮之间的间距 */
        }

        button:hover {
            background: #0056b3;
        }

        .markdown-body {
            background: transparent !important;
            font-size: 14px !important;
        }

        .typing-cursor::after {
            content: "|";
            animation: blink 1s step-end infinite;
            color: #007bff;
            margin-left: 2px;
        }

        @keyframes blink {
            0%, 100% { opacity: 1; }
            50% { opacity: 0; }
        }

        .hljs {
            background: #2d2d2d !important;
            border-radius: 6px;
            padding: 16px !important;
            font-size: 14px;
            line-height: 1.6;
        }

        pre {
            position: relative;
            margin: 12px 0;
            padding-left: 12px;
        }

        pre::before {
            content: ' ';
            position: absolute;
            left: 0;
            top: 0;
            height: 100%;
            width: 3px;
            background: linear-gradient(to bottom, #007bff, #00ff88);
        }
    </style>
</head>
<body>
<div class="chat-container">
    <div id="response" class="markdown-body"></div>
    <form id="chatForm" onsubmit="return sendMessage(event)">
        <input type="hidden" id="history" name="history">
        <div id="input-container">
            <textarea id="input" name="input" autocomplete="off" placeholder="输入消息..."></textarea>
            <button type="submit">发送</button>
            <button id="stopButton"  type="button" style="background-color: red; color: white; right: 90px;">停止</button>
        </div>
    </form>
</div>

<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script>
    const responseDiv = document.getElementById('response');
    const historyInput = document.getElementById('history');
    let isLockedBottom = true;
    let observer;
    let abortController = null; // 用于中断请求
    let lastInputValue = ''; // 用于保存上次发送的内容
    // 增强滚动控制
    const scrollManager = {
        init() {
            // 滚动位置跟踪
            responseDiv.addEventListener('scroll', _.throttle(() => {
                const threshold = 50;
                isLockedBottom = responseDiv.scrollTop + responseDiv.clientHeight
                    >= responseDiv.scrollHeight - threshold;
            }, 200));

            // DOM变化监听
            observer = new MutationObserver(_.debounce(() => {
                if (isLockedBottom) this.scrollToBottom();
            }, 50));
            observer.observe(responseDiv, { childList: true, subtree: true });
        },

        lastScrollTime: 0,

        scrollToBottom(force = false) {
            const now = Date.now();
            if (now - this.lastScrollTime < 100 && !force) return;

            this.lastScrollTime = now;
            responseDiv.scrollTo({
                top: responseDiv.scrollHeight,
                behavior: 'smooth'
            });
        }
    };
    scrollManager.init();

    class Typewriter {
        constructor(element) {
            this.element = element;
            this.buffer = '';
            this.isCodeBlock = false;
            this.renderQueue = [];
            this.isRendering = false;
        }

        async addText(text) {
            this.buffer += text;
            this.renderQueue.push(text);
            if (!this.isRendering) this.processQueue();
        }

        async processQueue() {
            this.isRendering = true;
            while (this.renderQueue.length > 0) {
                const text = this.renderQueue.shift();
                await this.renderText(text);
            }
            this.isRendering = false;
            this.element.classList.remove('typing-cursor');
            scrollManager.scrollToBottom(true);
        }

        async renderText(text) {
            // 创建临时容器
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = marked.parse(this.buffer);
            const newNodes = Array.from(tempDiv.childNodes);

            // 增量更新DOM
            this.element.replaceChildren(...newNodes);

            // 高亮逻辑优化
            if (this.isCodeBlock) {
                this.highlightCodeBlocks();
                await this.delay(50);
            } else {
                this.element.classList.add('typing-cursor');
                await this.delay(30);
            }

            // 智能滚动控制
            this.manageScroll();
        }
        highlightCodeBlocks() {
            const codeBlocks = this.element.querySelectorAll('pre code');
            codeBlocks.forEach(block => {
                if (!block.classList.contains('hljs')) {
                    hljs.highlightElement(block);
                }
            });
        }

        manageScroll() {
            const lastChild = this.element.lastElementChild;
            if (lastChild) {
                const lastPos = lastChild.getBoundingClientRect().top;
                const threshold = responseDiv.clientHeight * 0.3;

                if (lastPos < threshold) {
                    scrollManager.scrollToBottom();
                }
            }
        }

        delay(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }
    }

    function createMessageElement(role) {
        const messageDiv = document.createElement('div');
        messageDiv.className = `message ${role}-message`;
        const bubble = document.createElement('div');
        bubble.className = 'message-bubble';
        messageDiv.appendChild(bubble);
        return { messageDiv, bubble };
    }

    async function sendMessage(e) {
        lastInputValue='';
        e.preventDefault();
        const userInput = document.getElementById('input');
        const formData = new FormData();
        formData.append('input', userInput.value);
        formData.append('history', historyInput.value);

        // 初始化 AbortController
        abortController = new AbortController();
        const signal = abortController.signal;

        // 禁用输入控件
        userInput.disabled = true;
        e.target.querySelector('button[type="submit"]').disabled = true;

        try {
            // 创建用户消息
            const userMsg = createMessageElement('user');
            userMsg.bubble.innerHTML = marked.parse(userInput.value);
            responseDiv.appendChild(userMsg.messageDiv);
            scrollManager.scrollToBottom(true);

            // 创建AI消息
            const assistantMsg = createMessageElement('assistant');
            responseDiv.appendChild(assistantMsg.messageDiv);
            const typewriter = new Typewriter(assistantMsg.bubble);

            // 模拟API响应
            const response = await fetch('/query', {
                method: 'POST',
                body: formData,
                signal // 将信号绑定到请求
            });

            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            let buffer = '';
            let mdContent = '';

            while (true) {
                const { done, value } = await reader.read();
                if (done) break;

                buffer += decoder.decode(value, { stream: true });
                const chunks = buffer.split('\n');

                for (const chunk of chunks.slice(0, -1)) {
                    if (chunk) {
                        const data = JSON.parse(chunk);
                        if (data.done) {
                            historyInput.value = data.history || '';
                            assistantMsg.bubble.innerHTML = marked.parse(mdContent);
                            hljs.highlightAll();
                            scrollManager.scrollToBottom(true);
                        } else {
                            mdContent += data.token;
                            await typewriter.addText(data.token);
                        }
                    }
                }
                buffer = chunks[chunks.length - 1];
            }
        } catch (error) {
            if (error.name === 'AbortError') {
                console.log('请求已中断');
                lastInputValue=userInput.value
            } else {
                console.error('Error:', error);
                const errorMsg = createMessageElement('assistant');
                errorMsg.bubble.innerHTML = '<span style="color: red">连接出错，请重试</span>';
                responseDiv.appendChild(errorMsg.messageDiv);
                scrollManager.scrollToBottom(true);
            }
        } finally {
            userInput.disabled = false;
            userInput.value = lastInputValue;
            e.target.querySelector('button[type="submit"]').disabled = false;
            userInput.focus();
        }
    }
    // 停止按钮点击事件
    document.getElementById('stopButton').addEventListener('click', () => {
        if (abortController) {
            abortController.abort(); // 中断请求
            console.log('请求已中断，可以重新编辑发送');
            abortController = null; // 清空控制器

            // 恢复输入框和发送按钮的状态
            const userInput = document.getElementById('input');
            const sendButton = document.querySelector('#chatForm button[type="submit"]');
            userInput.disabled = false;
            sendButton.disabled = false;

            // 清空正在生成的消息（可选）
            // const assistantMessage = responseDiv.querySelector('.assistant-message:last-child');
            // if (assistantMessage) {
            //     assistantMessage.remove();
            // }
        }
    });

    // 输入框事件
    document.getElementById('input').addEventListener('keydown', e => {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            document.querySelector('button').click();
        }
    });
    // 输入框事件：动态调整高度
    // document.getElementById('input').addEventListener('input', function () {
    //     this.style.height = 'auto'; // 重置高度
    //     this.style.height = Math.min(this.scrollHeight, 150) + 'px'; // 限制最大高度
    // });

    document.getElementById('input').addEventListener('input', function() {
        this.style.height = 'auto';
        this.style.height = Math.min(this.scrollHeight, 150) + 'px'; // 限制最大高度
    });

    // 初始化代码高亮
    hljs.configure({
        languages: ['python', 'javascript', 'java'],
        ignoreUnescapedHTML: true
    });
</script>
</body>
</html>
